﻿module SymbolTableTypes
open Ast
open System
open System.Collections.Generic

type LinkedList<'a>
  with 
    member this.Head with get() = this.First.Value
    member this.Tail with get() = this.Last.Value
    member this.Pop () = 
      let first = this.Head
      do this.RemoveFirst()
      first
    member this.Push (value : 'a) =
      this.AddFirst(value) |> ignore
    member this.Enqueue (value : 'a) =
      this.AddLast(value) |> ignore

type List<'a> = LinkedList<'a>


type GlobalSymbols = Dictionary<string, ClassTable>

//and FieldOrMethod = 
//  | Field of Ast.Type 
//  | Method of Ast.Type list * ReturnType * BodyTable option ref

//and ClassSymbol =
//  {
//    Name          : string
//    Kind          : FieldOrMethod
//  }

and FieldSymbol =
  { 
    Name          : string
    Type          : Ast.Type
  }

and MethodSymbol =
  {
    Name          : string
    Infos         : Ast.Type list * ReturnType * BodyTable option ref
  }

and ClassTable = 
  {
    Name            : id
    Parent          : GlobalSymbols 
    Inherited       : string option
    Fields          : Dictionary<string, FieldSymbol>
    Methods         : Dictionary<string, MethodSymbol>
    ObjectLayout    : ResizeArray<string>
    
    //name * actual_class_owner
    DispatchVector  : ResizeArray<string * ref<id>>
  }

//and BodySymbolType = Param | Field of int

and BodySymbol =
  {
    Name          : string
    Type          : Ast.Type
    Alias         : string
  }

and AliasSymbol =
  {
    Name          : string
    Type          : Ast.Type
  }

and BodyTable =
  {
    Name          : id
    Container     : ClassTable
    Args          : Dictionary<string, BodySymbol>
    ReturnType    : ReturnType
    mutable Block : BlockTable option
    Aliases       : ResizeArray<BodySymbol>
  }

and BlockTable =  
  {
    Parent        : BodyOrBlock
    Symbols       : Dictionary<string, BodySymbol>
    Block         : Block
    Children      : List<BlockTable>
  }

and BodyOrBlock =
  | Body of BodyTable
  | Block of BlockTable

type GenericTable = 
  | GlobalSymbols of GlobalSymbols
  | ClassTable of ClassTable
  | BodyTable of BodyTable
  | BlockTable of BlockTable

let rec get_outer_block_table (t : GenericTable)= 
    match t with
  | GenericTable.GlobalSymbols _ -> failwith "no outer block_table"
  | GenericTable.ClassTable _ -> failwith "no outer block_table"
  | GenericTable.BodyTable t -> failwith "no outer block_table"
  | GenericTable.BlockTable t ->
    t.Parent

let rec get_body_table (t : GenericTable)= 
    match t with
  | GenericTable.GlobalSymbols _ -> failwith "no outer body_table"
  | GenericTable.ClassTable _ -> failwith "no outer body_table"
  | GenericTable.BodyTable t -> t
  | GenericTable.BlockTable t ->
    get_body_table (match t.Parent with | BodyOrBlock.Block b -> GenericTable.BlockTable b | BodyOrBlock.Body b -> GenericTable.BodyTable b )

let rec get_class_table (t : GenericTable) =
  match t with
  | GenericTable.GlobalSymbols _ -> failwith "no outer classTable"
  | GenericTable.ClassTable t -> t
  | _ -> 
    (get_body_table t).Container

let rec get_global_table (t : GenericTable) =
  match t with
  | GenericTable.GlobalSymbols t -> t
  | _ -> 
    (get_class_table t).Parent

let get_inherited_class_table (t : GenericTable) =
  let first_class = get_class_table t
  let globals = get_global_table t
  let rec get_super (t : ClassTable) =
    match t.Inherited with
    | None -> [t]
    | Some name when name = "$null$" -> [t]
    | Some name when name <> "$null$" ->
      globals.[t.Name] :: (get_super globals.[name])
  in
  get_super first_class

let get_inherited_field_names (t : GenericTable) =
  let class_tables = get_inherited_class_table t
  class_tables |> Seq.map (  
                              fun c_t ->
                                c_t.Fields.Values |> Seq.map (fun f -> f.Name)
                           ) |> Seq.concat |> Seq.toList

let get_inherited_methods (t : GenericTable) =
  let class_tables = get_inherited_class_table t
  class_tables |> Seq.map ( fun c_t -> c_t.Methods.Values ) |> Seq.concat |> Seq.toList

